#include "drv_beep.h"
#include <string.h>

#define BEEP_PIN                BSP_BEEP_PIN
#define BEEP_RING_CIRCLE        100
#define BEEP_ON                 BSP_BEEP_ACTIVE_LEVEL
#define BEEP_OFF                (!BEEP_ON)
 
struct rt_beep_device
{
    struct rt_device parent;
    rt_uint8_t status;
};
static struct rt_beep_device *beep_dev; 
static rt_ssize_t beep_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{    
    struct rt_beep_device *_dev;
    uint8_t status;

    RT_ASSERT(dev != RT_NULL);
    _dev = (struct rt_beep_device *)dev;
    status = *(uint8_t *)buffer;
    if(_dev->status != status)
    {
        _dev->status = status;
        rt_pin_write(BEEP_PIN, _dev->status);
    }
    return 1;
}
static rt_ssize_t beep_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    struct rt_beep_device *_dev;

    RT_ASSERT(dev != RT_NULL);
    _dev = (struct rt_beep_device *)dev;
    *(uint8_t *)buffer = _dev->status; 
    return 1;
}
static rt_err_t beep_control(rt_device_t dev, int cmd, void *args)
{
    struct rt_beep_device *_dev;
    rt_uint8_t status;

    RT_ASSERT(dev != RT_NULL);

    _dev = (struct rt_beep_device *)dev;

    switch(cmd)
    {
        case CMD_BEEP_OFF:
            if(_dev->status != BEEP_OFF)
            {
                status = BEEP_OFF;
                beep_write(dev, 0, &status, 1);
            }
            break;
        case CMD_BEEP_ON:
            if(_dev->status != BEEP_ON)
            {
                status = BEEP_ON;
                beep_write(dev, 0, &status, 1);
            }
            break;
        case CMD_BEEP_TOGGLE:
            status = !_dev->status;
            beep_write(dev, 0, &status, 1);
            break;
        case CMD_BEEP_RING:
            status = BEEP_ON;
            beep_write(dev, 0, &status, 1);
            rt_thread_mdelay(BEEP_RING_CIRCLE);
            status = BEEP_OFF;
            beep_write(dev, 0, &status, 1);
            break;
    }
}
static int rt_hw_beep_init(void)
{
    beep_dev = (struct rt_beep_device *)rt_malloc(sizeof(struct rt_beep_device));
    RT_ASSERT(beep_dev != RT_NULL);
    rt_pin_mode(BEEP_PIN, PIN_MODE_OUTPUT);
    rt_pin_write(BEEP_PIN, BEEP_OFF);
    beep_dev->status = BEEP_OFF;
    beep_dev->parent.type = RT_Device_Class_Char;
    beep_dev->parent.init = RT_NULL;
    beep_dev->parent.open = RT_NULL;
    beep_dev->parent.close = RT_NULL;
    beep_dev->parent.write = beep_write;
    beep_dev->parent.read = beep_read;
    beep_dev->parent.control = beep_control;
    rt_device_register(&beep_dev->parent, "beep", RT_DEVICE_FLAG_RDWR);
    return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_beep_init);

static void beep_ctrl(int argc, char **argv)
{
    rt_device_t dev;
    if(argc != 3)
    {
        rt_kprintf("Usage: %s <beep> <on|off|toggle|ring>\n", argv[0]);
        return;
    }
    dev = rt_device_find(argv[1]);
    if(dev == RT_NULL)
    {
        rt_kprintf("relay device %s not found\n", argv[1]);
        return;
    }
    rt_device_open(dev, RT_DEVICE_OFLAG_OPEN);
    if(!strcmp(argv[2], "on"))
    {
        rt_device_control(dev, CMD_BEEP_ON, RT_NULL);
    }
    else if(!strcmp(argv[2], "off"))
    {
        rt_device_control(dev, CMD_BEEP_OFF, RT_NULL);
    }
    else if(!strcmp(argv[2], "toggle"))
    {
        rt_device_control(dev, CMD_BEEP_TOGGLE, RT_NULL);
    }
    else if(!strcmp(argv[2], "ring"))
    {
        rt_device_control(dev, CMD_BEEP_RING, RT_NULL);
    }
    else
    {
        rt_kprintf("Usage: %s <beep> <on|off|toggle|ring>\n", argv[0]);
    }
    rt_device_close(dev);
}
MSH_CMD_EXPORT(beep_ctrl, beep control);
